// 实现一个模块规范

const path = require('path');
const fs = require('fs');
const vm = require('vm');

class Module {
  constructor(id) {
    this.id = id; // 每个模块的id即当前运行环境下的绝对路径
    this.exports = {};
  }

  // 缓存对象
  static _cache = Object.create(null);

  // 包装
  static warp = function (script) {
    return `(function (exports, require, module, __filename, __dirname) {
      ${script}
    })`;
  };

  // 解析文件名
  static _resolveFilename(path) {
    return Module._resolveFilePath(path);
  }

  // 解析文件路径
  /* 整体思路：
   * 1. 判断是否是绝对路径
   * 2. 如果路径中已经有扩展名，并且在 _fileExtentions列表中，则查看该文件是否存在
   * 3. 如果没有扩展名，或者扩展名不在 _fileExtentions列表中，则依次尝试 _fileExtentions列表中的扩展名，看有没有符合条件的文件
   * 4. 如果文件存在返回文件名，如果不存在，则返回空字符串
   */
  static _resolveFilePath(request) {
    let resolvePath = '';
    let parsePath = '';
    let fileExists = false;

    if (path.isAbsolute(request)) {
      resolvePath = resolvePath;
    } else {
      resolvePath = path.resolve(__dirname, request);
    }
    let ext = path.extname(resolvePath);
    if (ext) {
      if (!Module._fileExtentions.includes(ext)) {
        parsePath = tryExtensions(resolvePath);
      } else {
        try {
          parsePath = tryRealPath(resolvePath);
        } catch (e) {
          console.error(e);
        }
      }
    } else {
      parsePath = tryExtensions(resolvePath);
    }

    return parsePath;
  }

  // 模块自动查找，一层层的向上遍历 node_modules

  // 加载模块
  static _rolad(path) {
    let filename = Module._resolveFilename(path);

    // 读取缓存
    let cacheModule = Module._cache[filename];
    if (cacheModule) {
      return cacheModule.exports;
    }

    // 无缓存就加入缓存中
    let module = new Module(filename);
    Module._cache[filename] = module;

    tryModuleLoad(module, filename);

    return module.exports;
  }

  // 处理js json node
  static __fileExtentions = {
    js: function (module, filename) {
      let content = fs.readFileSync(filename, 'utf-8');
      module._compile(_stripBOM(content), filename);
    },
    '.json': function (module) {
      let content = fs.readFileSync(filename, 'utf-8');
      module.exports = JSON.parse(content);
    },
    '.node': function (module, filename) {},
  };

  // 编译模块
  _compile(content, filename) {
    let wrapper = Module.warp(content);

    let compiledWrapper = vm.runInThisContext(wrapper, {
      filename: filename,
      lineOffset: 0,
      displayErrors: true,
    });

    return compiledWrapper.call(this.exports, this.exports, myRequire, this);
  }

  require(path) {
    return Module._rolad(path);
  }
}

// 处理文件从gbk转utf-8时得BOM头问题
function _stripBOM(content) {
  if (content.charCodeAt(0) === 0xfeff) {
    content = content.slice(1);
  }
  return content;
}

// 尝试加载模块
function tryModuleLoad(module, filename) {
  module.load(filename);
}

function tryRealPath(path) {
  return fs.realpathSync(path);
}

function tryExtensions(resolvePath) {
  let parsePath = '';
  for (let ext of Module._fileExtentions) {
    let requestPath = resolvePath + ext;
    try {
      if (tryRealPath(requestPath)) {
        parsePath = requestPath;
        fileExists = true;
        break;
      }
    } catch (e) {
      continue;
    }
  }
  return parsePath;
}

// 主函数 使用
function myRequire(path) {
  let module = new Module();
  return module.require(path);
}

// 导出作为全局
window.myRequire = myRequire;
